// Copyright (c) 2018-2019,  Jiang Si. For more information see 'LICENSE'
/*
	FrameGraph - vulkan abstraction layer that represent frame as a task graph.
*/
#pragma once



#include "GSStl/Common.h"

#include "Meleon/Defines.h"
#include "Meleon/Vulkan/Create/VulkanDeviceExt.h"
#include "Meleon/Public/FrameGraph.h"
#include "Meleon/Public/Pipeline.h"
#include "Meleon/Public/PipelineCompiler.h"
#include "Meleon/Public/MemoryDesc.h"
#include "Meleon/Public/SamplerDesc.h"
#include "Meleon/Public/ImageDesc.h"


using namespace GS;
using namespace GSC;

struct ImageResDesc
{
	EImage				imageType = Default;
	uint3				dimension;	// width, height, depth, layers
	EPixelFormat		format = Default;
	EImageUsage			usage = Default;
	ImageLayer			arrayLayers;
	MipmapLevel			maxLevel;
	MultiSamples		samples;	// if > 1 then enabled multisampling
	EQueueUsage			queues = Default;
	//bool				isLogical	= false;
	bool				isExternal = false;
};

struct SamplerResDesc
{
	using AddressMode3	= Vec< EAddressMode, 3 >;

	EFilter					magFilter = EFilter::Nearest;
	EFilter					minFilter = EFilter::Nearest;
	EMipmapFilter			mipmapMode = EMipmapFilter::Nearest;
	AddressMode3			addressMode = { EAddressMode::Repeat, EAddressMode::Repeat, EAddressMode::Repeat };
	float					mipLodBias = 0.0f;
	Optional<float>			maxAnisotropy;
	Optional<ECompareOp>	compareOp;
	float					minLod = -1000.0f;
	float					maxLod = 1000.0f;
	EBorderColor			borderColor = EBorderColor::FloatTransparentBlack;
	bool					unnormalizedCoordinates = false;

	SamplerResDesc& SetAddressMode(EAddressMode uvw)
	{
		addressMode = { uvw,uvw,uvw};
		return *this;
	}

	//SamplerResDesc& SetFilter(EFilter mag, EFilter min, EMipmapFilter mipmap)
	//{
	//	addressMode = aMode;
	//	return *this;
	//}
};


struct IMeleon
{
	virtual ~IMeleon() {}

	//create
	virtual bool CreateVulkan(
		UniquePtr<IVulkanSurface>&& surface,
		StringView					appName,
		StringView					engineName,
		const uint					version,
		StringView					deviceName,
		ArrayView<VulkanDevice::QueueCreateInfo>	queues,
		ArrayView<const char*>		instanceLayers,
		ArrayView<const char*>		instanceExtensions,
		ArrayView<const char*>		deviceExtensions) = 0;

	virtual GS::FrameGraph  CreateFrameGraph(const GS::VulkanDeviceInfo &) = 0;

	virtual SharedPtr<IPipelineCompiler> CreatePipelineCompiler(PhysicalDeviceVk_t physicalDevice, DeviceVk_t device) = 0;

	virtual bool CreateDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagsEXT severity, VulkanDeviceExt::DebugReport_t&& callback) = 0;

	virtual ArrayView<const char*> GetRecomendedInstanceLayers() = 0;
	virtual ArrayView<const char*>  GetRecomendedInstanceExtensions() = 0;
	virtual ArrayView<const char*>  GetAllDeviceExtensions() = 0;

	//get VK param
	virtual VkInstance					GetVkInstance()	const = 0;
	virtual VkPhysicalDevice			GetVkPhysicalDevice()		const = 0;
	virtual VkDevice					GetVkDevice()				const = 0;
	virtual VkSurfaceKHR				GetVkSurface()				const = 0;
	virtual ArrayView<VulkanDevice::VulkanQueue>		GetVkQueues()	const = 0;
	
	//pipeline Desc
	virtual void	BeginGraphicsPipelineDesc(INOUT HGraphicPipeline& pipeline) = 0;
	virtual void	AddShader(HGraphicPipeline pipeline,EShader shaderType, EShaderLangFormat fmt, StringView entry, String&& src, StringView dbgName = Default) = 0;
	virtual void	AddShader(HGraphicPipeline pipeline, EShader shaderType, EShaderLangFormat fmt, StringView entry, Array<uint8_t>&& bin, StringView dbgName = Default) = 0;
	virtual void	AddShader(HGraphicPipeline pipeline, EShader shaderType, EShaderLangFormat fmt, StringView entry, Array<uint>&& bin, StringView dbgName = Default) = 0;
	virtual void	AddShader(HGraphicPipeline pipeline, EShader shaderType, EShaderLangFormat fmt, const PipelineDescription::VkShaderPtr& module) = 0;
	virtual GPipelineID	EndGraphicsPipelineDesc(HGraphicPipeline pipeline) = 0;

	virtual void	BeginComputePipelineDesc(INOUT HComputePipeline& pipeline) = 0;
	virtual void	AddShader(HComputePipeline pipeline,EShaderLangFormat fmt, StringView entry, String&& src, StringView dbgName=Default)=0;
	virtual CPipelineID	EndComputePipelineDesc(HComputePipeline pipeline) = 0;
	
	virtual ImageID	CreateImage(const ImageResDesc& imageDesc)=0;
	virtual ImageID CreateImage(const ImageResDesc& desc, const MemoryDesc& mem, StringView dbgName) = 0;
	virtual SamplerID  CreateSampler(const SamplerDesc& desc, StringView dbgName=Default) = 0;

	virtual RawImageID GetSwapchainImage(RawSwapchainID swapchainId, ESwapchainImage type) = 0;

	//renderPass
	virtual void	BeginRenderPass(INOUT HRenderPass& renderPass, uint2 renderArea) = 0;
	// render target
	virtual	void	AddTarget(HRenderPass renderPass, RenderTargetID id, RawImageID image) = 0;
	virtual	void	AddTarget(HRenderPass renderPass, RenderTargetID id, RawImageID image, EAttachmentLoadOp loadOp, EAttachmentStoreOp storeOp) = 0;
	virtual	void	AddTarget(HRenderPass renderPass, RenderTargetID id, RawImageID image, const RenderPassDesc::ClearValue_t& clearValue, EAttachmentStoreOp storeOp) = 0;
	virtual	void	AddTarget(HRenderPass renderPass, RenderTargetID id, RawImageID image, const ImageViewDesc& desc, EAttachmentLoadOp loadOp, EAttachmentStoreOp storeOp) = 0;
	virtual	void	AddTarget(HRenderPass renderPass, RenderTargetID id, RawImageID image, const ImageViewDesc& desc, const RenderPassDesc::ClearValue_t& clearValue, EAttachmentStoreOp storeOp)=0;
	virtual void	AddViewport(HRenderPass renderPass, float2 area,float minDepth = 0.0f, float maxDepth = 1.0f, ArrayView<EShadingRatePalette> palette = Default) = 0;
	virtual void	SetDepthTestEnabled(HRenderPass renderPass, bool flag) = 0;
	virtual LogicalPassID EndRenderPass(HRenderPass renderPass)=0;

	virtual void    SetFrameGraph(const FrameGraph& fg)=0;
	virtual void	SetVulkanDevice(VulkanDevice* vkDevice) = 0;

	//cmd BUFF
	virtual void	SetSubmitCmdBufferNums(uint16_t numsCmdBufs)=0;
	virtual void	FrameGraphWaitCmdBuffer() = 0;
	virtual ICommandBuffer* GetCommandBufferOp() = 0;
	virtual void	FrameGraphBegin(EQueueType queueType) = 0;
	virtual void	FrameGraphExecute() = 0;
	virtual bool	FrameGraphEnd() = 0;

	virtual void Destroy() = 0;
};

extern "C"
{
	Meleon_API IMeleon* CreateMeleon();
}

// Meleon DLL Exports.
typedef IMeleon* (*PFN_CreateMeleon)();